package com.zbkj.service.websocket;
import cn.hutool.json.JSONUtil;
import com.zbkj.common.config.CrmebConfig;
import com.zbkj.common.netty.AcceptDataContent;
import com.zbkj.common.utils.RequestUtil;
import com.zbkj.service.service.SystemAttachmentService;
import com.zbkj.service.util.ResponseRouterUtil;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.http.HttpClientCodec;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.websocketx.*;
import io.netty.handler.timeout.IdleStateHandler;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.servlet.http.HttpServletRequest;
import java.net.URI;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;

@Slf4j
@Component
public class WebSocketClient {

    private final String host = "localhost";
    private final int port = 19090;
    private final String path = "/ws";

    private EventLoopGroup group;
    private final AtomicReference<Channel> channelRef = new AtomicReference<>();
    private final AtomicBoolean isConnected = new AtomicBoolean(false);
    private final AtomicBoolean isConnecting = new AtomicBoolean(false);
    private final AtomicBoolean handshakeComplete = new AtomicBoolean(false);

    @Autowired
    CrmebConfig crmebConfig;

//    @PostConstruct
    public void init() {
        connect();
    }

    @PreDestroy
    public void destroy() {
        if (group != null) {
            group.shutdownGracefully();
        }
    }

    public synchronized void connect() {
        if (isConnected.get() || isConnecting.get()) {
            return;
        }

        isConnecting.set(true);
        log.info("Attempting to connect to WebSocket server...");

        group = new NioEventLoopGroup(1); // 使用单线程组
        try {
            Bootstrap bootstrap = new Bootstrap();
            bootstrap.group(group)
                .channel(NioSocketChannel.class)
                .handler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    protected void initChannel(SocketChannel ch) {
                        ChannelPipeline pipeline = ch.pipeline();
                        pipeline.addLast(new HttpClientCodec());
                        pipeline.addLast(new HttpObjectAggregator(65536));
                        pipeline.addLast(new WebSocketClientProtocolHandler(
                            URI.create("ws://" + host + ":" + port + path),
                            WebSocketVersion.V13, null, false, null, 65536));
                        pipeline.addLast(new IdleStateHandler(0, 30, 0, TimeUnit.SECONDS));
                        pipeline.addLast(new WebSocketClientHandler());
                    }
                });

            ChannelFuture future = bootstrap.connect(host, port);
            future.addListener((ChannelFutureListener) f -> {
                if (f.isSuccess()) {
                    channelRef.set(f.channel());
                    log.info("TCP connection established, waiting for WebSocket handshake...");
                } else {
                    log.error("Connection failed, will retry in 10 seconds", f.cause());
                    scheduleReconnect(10);
                    isConnecting.set(false);
                }
            });
        } catch (Exception e) {
            log.error("Connection error", e);
            isConnecting.set(false);
            scheduleReconnect(10);
        }
    }

    private void scheduleReconnect(int delaySeconds) {
        if (group != null) {
            group.schedule(() -> {
                if (!isConnected.get()) {
                    connect();
                }
            }, delaySeconds, TimeUnit.SECONDS);
        }
    }

    public void sendMessage(AcceptDataContent acceptDataContent) {
        String jsonStr = JSONUtil.toJsonStr(acceptDataContent);
        try {
            HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
            jsonStr = ResponseRouterUtil.filter(jsonStr, RequestUtil.getUri(request), crmebConfig);
        } catch (Exception e) {
            log.error("转换路径异常", e);
        }
        // 处理
        sendMessage(jsonStr);
    }
    public synchronized void sendMessage(String message) {
        if (!isConnected.get() || !handshakeComplete.get()) {
            log.warn("Not ready to send message, connection status: {}, handshake status: {}",
                isConnected.get(), handshakeComplete.get());
            return;
        }

        Channel channel = channelRef.get();
        if (channel != null && channel.isActive()) {
            channel.writeAndFlush(new TextWebSocketFrame(message))
                .addListener((ChannelFutureListener) future -> {
                    if (!future.isSuccess()) {
                        log.error("Failed to send message", future.cause());
                        handleSendFailure();
                    }
                });
        } else {
            log.error("Channel is not active, cannot send message");
            handleSendFailure();
        }
    }

    private void handleSendFailure() {
        isConnected.set(false);
        handshakeComplete.set(false);
        Channel channel = channelRef.get();
        if (channel != null) {
            channel.close();
        }
        scheduleReconnect(5);
    }

    public void sendPing() {
        if (isConnected.get() && handshakeComplete.get()) {
            sendMessage("PING");
        }
    }

    private class WebSocketClientHandler extends SimpleChannelInboundHandler<Object> {

        @Override
        public void channelActive(ChannelHandlerContext ctx) {
            log.info("TCP connection established");
            // 不在这里发送消息，等待握手完成
        }

        @Override
        public void userEventTriggered(ChannelHandlerContext ctx, Object evt) {
            if (evt instanceof WebSocketClientProtocolHandler.ClientHandshakeStateEvent) {
                WebSocketClientProtocolHandler.ClientHandshakeStateEvent handshakeEvent =
                    (WebSocketClientProtocolHandler.ClientHandshakeStateEvent) evt;

                if (handshakeEvent == WebSocketClientProtocolHandler.ClientHandshakeStateEvent.HANDSHAKE_COMPLETE) {
                    log.info("WebSocket handshake completed");
                    isConnected.set(true);
                    handshakeComplete.set(true);
                    isConnecting.set(false);
                    // 握手完成后再开始发送心跳
                    sendPing();
                } else if (handshakeEvent == WebSocketClientProtocolHandler.ClientHandshakeStateEvent.HANDSHAKE_TIMEOUT) {
                    log.error("WebSocket handshake timeout");
                    ctx.close();
                    isConnected.set(false);
                    handshakeComplete.set(false);
                    scheduleReconnect(5);
                }
            } else if (handshakeComplete.get()) {
                // 空闲事件触发时发送PING（仅在握手完成后）
                sendPing();
            }
        }

        @Override
        protected void channelRead0(ChannelHandlerContext ctx, Object msg) {
            if (msg instanceof TextWebSocketFrame) {
                String text = ((TextWebSocketFrame) msg).text();
                log.info("Received message from server: {}", text);

                if ("PONG".equals(text)) {
                    log.debug("Received PONG from server");
                }
            } else if (msg instanceof PongWebSocketFrame) {
                log.debug("Received PONG frame from server");
            } else if (msg instanceof CloseWebSocketFrame) {
                log.info("Received close frame from server");
                ctx.close();
            }
        }

        @Override
        public void channelInactive(ChannelHandlerContext ctx) {
            log.warn("WebSocket connection closed");
            isConnected.set(false);
            handshakeComplete.set(false);
            scheduleReconnect(5);
        }

        @Override
        public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
            log.error("WebSocket error", cause);
            ctx.close();
            isConnected.set(false);
            handshakeComplete.set(false);
            scheduleReconnect(5);
        }
    }
}